const pageNum = 4;
let recentPage = 0;
let pageContainer = $("pageContainer");
let serialCode = 0;
let youngVisualizer = new YoungVisualizer2(visualizerElements, events);
let events = JSON.parse(`[
    {
        "jvmObjEdenS0Size": 681568,
        "jvmObjOldSize": 0,
        "edenCapacity": 1118480,
        "survivorCapacity": 139810,
        "oldCapacity": 2796202,
        "type": "VMInit"
    },
    {
        "objTag": 1,
        "objSignature": "LMain;",
        "objSize": 16,
        "objPosition": "Eden",
        "userEdenS0Size": 16,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 681568,
        "jvmObjOldSize": 0,
        "type": "ObjectAlloc"
    },
    {
        "objTag": 2,
        "objSignature": "Ljava/lang/String;",
        "objSize": 24,
        "objPosition": "Eden",
        "userEdenS0Size": 40,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 681568,
        "jvmObjOldSize": 0,
        "type": "ObjectAlloc"
    },
    {
        "objTag": 3,
        "objSignature": "Ljava/util/ArrayList;",
        "objSize": 24,
        "objPosition": "Eden",
        "userEdenS0Size": 64,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 681568,
        "jvmObjOldSize": 0,
        "type": "ObjectAlloc"
    },
    {
        "objTag": 4,
        "objSignature": "[Ljava/lang/String;",
        "objSize": 56,
        "objPosition": "Eden",
        "userEdenS0Size": 120,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 681568,
        "jvmObjOldSize": 0,
        "type": "ObjectAlloc"
    },
    {
        "type": "MinorGCStart"
    },
    {
        "objTag": 1,
        "objPosition": "Eden",
        "type": "Mark"
    },
    {
        "objTag": 2,
        "objPosition": "Eden",
        "type": "Mark"
    },
    {
        "objTag": 3,
        "objPosition": "Eden",
        "type": "Mark"
    },
    {
        "objTag": 4,
        "objPosition": "Eden",
        "type": "Mark"
    },
    {
        "copyTo": "Survivor0",
        "toSObjTags": [],
        "toOldObjTags": [],
        "userEdenS0Size": 120,
        "userS1Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": -1,
        "jvmS1Size": -1,
        "jvmObjOldSize": -1,
        "type": "MinorCopy"
    },
    {
        "userEdenS0Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 770048,
        "jvmObjOldSize": 8192,
        "type": "MinorDelete"
    },
    {
        "type": "MinorGCFinish"
    },
    {
        "type": "FullGCStart"
    },
    {
        "userEdenS0Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525312,
        "type": "FullDelete"
    },
    {
        "type": "FullCompact"
    },
    {
        "toOldObjTags": [],
        "userEdenS0Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525312,
        "type": "FullMove"
    },
    {
        "type": "FullGCFinish"
    },
    {
        "objTag": 5,
        "objSignature": "LMain;",
        "objSize": 16,
        "objPosition": "Eden",
        "userEdenS0Size": 16,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525312,
        "type": "ObjectAlloc"
    },
    {
        "type": "MinorGCStart"
    },
    {
        "copyTo": "Survivor1",
        "toSObjTags": [
            5
        ],
        "toOldObjTags": [],
        "userEdenS0Size": 16,
        "userS1Size": 16,
        "userOldSize": 0,
        "jvmObjEdenS0Size": -1,
        "jvmS1Size": -1,
        "jvmObjOldSize": -1,
        "type": "MinorCopy"
    },
    {
        "userEdenS0Size": 16,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 32752,
        "jvmObjOldSize": 525312,
        "type": "MinorDelete"
    },
    {
        "type": "MinorGCFinish"
    },
    {
        "type": "FullGCStart"
    },
    {
        "userEdenS0Size": 0,
        "userOldSize": 16,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525296,
        "type": "FullDelete"
    },
    {
        "type": "FullCompact"
    },
    {
        "toOldObjTags": [
            5
        ],
        "userEdenS0Size": 0,
        "userOldSize": 16,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525296,
        "type": "FullMove"
    },
    {
        "type": "FullGCFinish"
    },
    {
        "type": "MinorGCStart"
    },
    {
        "copyTo": "Survivor0",
        "toSObjTags": [],
        "toOldObjTags": [],
        "userEdenS0Size": 0,
        "userS1Size": 0,
        "userOldSize": 16,
        "jvmObjEdenS0Size": -1,
        "jvmS1Size": -1,
        "jvmObjOldSize": -1,
        "type": "MinorCopy"
    },
    {
        "userEdenS0Size": 0,
        "userOldSize": 16,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 525296,
        "type": "MinorDelete"
    },
    {
        "type": "MinorGCFinish"
    },
    {
        "type": "FullGCStart"
    },
    {
        "objTag": 5,
        "objPosition": "Young",
        "type": "Mark"
    },
    {
        "userEdenS0Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 524288,
        "type": "FullDelete"
    },
    {
        "type": "FullCompact"
    },
    {
        "toOldObjTags": [],
        "userEdenS0Size": 0,
        "userOldSize": 0,
        "jvmObjEdenS0Size": 0,
        "jvmObjOldSize": 524288,
        "type": "FullMove"
    },
    {
        "type": "FullGCFinish"
    }
]`);

$("right").addEventListener("click", () => {
    if (recentPage == pageNum - 1) return;

    let percent = pageContainer.style.right;
    if (percent == "") percent = "0%";
    percent = addPercent(percent, 100);
    percent = percent + "%";
    pageContainer.style.right = percent;
    recentPage++;
});

$("left").addEventListener("click", () => {
    if (recentPage == 0) return;

    let percent = pageContainer.style.right;
    if (percent == "") percent = "0%";
    percent = minusPercent(percent, 100);
    percent = percent + "%";
    pageContainer.style.right = percent;
    recentPage--;
});

class Code {
    constructor(code) {
        this.code = code;
    }

    init() {
        let index = this.code.indexOf("public class");
        if (index == -1) {
            this.errorcode = -1;
            throw new Error("No public class exception");
        }
        let temp = this.code.substring(index + "public class".length + 1);
        let i = 0;
        for (i; i < temp.length; i++) {
            if (temp[i] == ' ') {
                break;
            }
        }
        this.publicName = temp.substring(0, i);

        index = this.code.indexOf("public static void main(String[]");
        if (index == -1) {
            this.errorcode = -2;
            throw new Error("No main function exception");
        }
        temp = this.code.substring(0, index);
        let classIndex = temp.lastIndexOf("class");
        i = classIndex = classIndex + "class ".length;
        for (i; i < temp.length; i++) {
            if (temp[i] == ' ') {
                break;
            }
        }
        this.mainClassName = temp.substring(classIndex, i);

        this.errorcode = 0;
    }
}


$("submit").addEventListener("click", () => {
    let code = $("code-text").value;
    // Verify Code
    let codeObj = new Code(code);
    try {
        codeObj.init();
        postCode("http://localhost:8080/analysis/codes", code, (responseText) => {
            serialCode = JSON.parse(responseText).data.serialCode;
            alert("Submit Successful");
        });
    } catch (exception) {
        console.log(exception.message);
        if (codeObj.errorcode == -1) {
            // No public class, hint user
            alert("A java file need one public class, please append 'public' modifier");
        } else if (codeObj.errorcode = -2) {
            // No main function, hint user
            alert("No main function available, please check your code");
        }
    }
});

$("analyse").addEventListener("click", () => {
    getAnalysis("http://localhost:8080/analysis/", 1, (responseText) => {
        alert("Analyse Successful\n(You can check result in console)");
        jsonObj = JSON.parse(responseText);
        events = jsonObj.data;
        
        youngVisualizer.events = events;
    });
})

$("young-detail").addEventListener("click", () => {
    // document.getElementsByTagName("article")[0].classList.remove("disappear-scalex");
    // document.getElementsByTagName("article")[0].classList.add("appear-scalex");
    let young = $("analysis-young");
    young.style.transform = "scale(1)";
    young.style.transition = "1s";
    young.style.transitionDelay = ".5s";
    young.style.opacity = "1";

    let main = $("main");
    // mainContainer.style.transform = "rotateZ(90deg)";
    main.style.transform = "perspective(1000px) rotateX(70deg) translateY(80%)";
    main.style.transition = "1s";

    let title = $("title");
    title.style.transform = "translateY(-80px)";
    title.style.opacity = "0";
    title.style.transition = "1s";

    let delay = 1;
    for (const label of $("aside-young").children) {
        label.style.transform = "scale(1)";
        label.style.transitionDelay = `${delay++ * 0.1 + 1.2}s`;
    }
})

// Click "young-datail" function changeed labels' transition-delay
// So that original hover reaction cannot work
// So I reset it when transitionend
for (const label of $("aside-young").children) {
    label.addEventListener("transitionend", () => {
        label.style.transitionDelay = "0s";
    })
}

$("back-young").addEventListener("click", () => {
    let young = $("analysis-young");
    young.style.transform = "scale(0)";
    young.style.transition = "1s";
    young.style.opacity = "0";

    let main = $("main");
    main.style.transform = "scale(1)";
    main.style.transitionDelay = ".3s";

    let title = $("title");
    title.style.transform = "";
    title.style.opacity = "1";
    title.style.transitionDelay = ".3s";

    for (const label of $("aside-young").children) {
        label.style.transform = "scale(0)";
    }
})

// $("reset-young").addEventListener("click", () => {
//     $("eden-container").innerHTML = "";
//     $("s1-container").innerHTML = "";
//     $("s2-container").innerHTML = "";
//     $("old-container").innerHTML = "";
//     startIndex = 0;
// })

// function createObjElemContent(klass, size, name) {
//     return `<label>${klass}</label>
//     <label>${name}</label>
//     <label>${size}</label>`;
// }

// function createObjElem(container, tag, klass, size, name) {
//     let content = createObjElemContent(klass, size, name);
//     let div = document.createElement("div");
//     div.id = `${container.id}-${tag}`;
//     div.innerHTML = content;
//     return div;
// }

// function deleteUnreachableElem() {
//     for (const objElem of unreachables) {
//         objElem.innerHTML = "";
//         objElem.style.display = "none";
//     }
// }

// function deleteUnreachableElem(container) {
//     for (const objElem of unreachables) {
//         container.removeChild(objElem);
//     }
// }

// function clearEdenAndCompanionElems() {
//     edenContainer.innerHTML = "";
//     companionSurvivorContainer.innerHTML = "";
// }

// function tagUnreachable(container, tag) {
//     let id = `${container.id}-${tag}`;
//     let objElem = $(id);
//     if (objElem == null) return;
//     setUnreachableTag(objElem);
//     addUnreachables(objElem);
// }

// function setUnreachableTag(objElem) {
//     objElem.style.background = "#ff7675";
//     for (const label of objElem.children) {
//         label.style.color = "white";
//     }
// }

// function addUnreachables(objElem) {
//     unreachables.push(objElem);
// }

// function addDisappearAnim(objElem) {
//     objElem.style.animationName = "disappear-scalex";
//     objElem.style.animationDuration = "0.5s";
//     objElem.style.animationFillMode = "forwards";
// }

// function addAppearAnim(objElem) {
//     objElem.style.animationName = "appear-scalex";
//     objElem.style.animationDuration = "0.5s";
//     objElem.style.animationFillMode = "forwards";
// }

// function moveObjElem(fromContainer, toContainer, tags) {
//     for (const tag of tags) {
//         let fromId = `${fromContainer.id}-${tag}`;
//         let fromObjElem = $(fromId);
//         if (fromObjElem == null) return;
//         // Push into unreachables
//         unreachables.push(fromObjElem);
//         // Get info
//         let klass = fromObjElem.children[0].innerHTML;
//         let name = fromObjElem.children[1].innerHTML;
//         let size = fromObjElem.children[2].innerHTML;
//         addDisappearAnim(fromObjElem);
//         // Create toElems in toContainer
//         let toObjElem = createObjElem(toContainer, tag, klass, size, name);
//         addAppearAnim(toObjElem);
//         toContainer.appendChild(toObjElem);
//     }
// }

// function copyObjElem(fromContainer, toContainer, tags) {
//     for (const tag of tags) {
//         let fromId = `${fromContainer.id}-${tag}`;
//         let fromObjElem = $(fromId);
//         if (fromObjElem == null) return;
//         // Get info
//         let klass = fromObjElem.children[0].innerHTML;
//         let name = fromObjElem.children[1].innerHTML;
//         let size = fromObjElem.children[2].innerHTML;
//         // Create toElems in toContainer
//         let toObjElem = createObjElem(toContainer, tag, klass, size, name);
//         addAppearAnim(toObjElem);
//         toContainer.appendChild(toObjElem);
//     }
// }

// function addEdenSize(size, capacity) {
//     if (typeof size == "string") {
//         edenSizeBom.innerHTML = `Size: ${edenSize + parseInt(size)}/${capacity} Bytes`;
//         edenSize += parseInt(size);
//     } else if (typeof size == "number") {
//         edenSizeBom.innerHTML = `Size: ${edenSize + size}/${capacity} Bytes`;
//         edenSize += size;
//     }
// }

// function addOldSize(size) {
//     if (typeof size == "string") {
//         oldSizeBom.innerHTML = `Size: ${oldSize + parseInt(size)} Bytes`;
//         oldSize += parseInt(size);
//     } else if (typeof size == "number") {
//         oldSizeBom.innerHTML = `Size: ${oldSize + size} Bytes`;
//         oldSize += size;
//     }
// }

// function setSize(sizeBom, size, capacity) {
//     sizeBom.innerHTML = `Size: ${size}/${capacity} Bytes`;
// }

// function setJvmSize(jvmSizeBom, size) {
//     jvmSizeBom.innerHTML = `JVM created objects: ${size} Bytes`;
// }

// let edenContainer = $("eden-container");
// let survivorContainer = $("s2-container");
// let companionSurvivorContainer = $("s1-container");
// let oldContainer = $("old-container");
// let eventYoung = $("event-young");
// let infoYoung = $("info-young");
// let edenSize = 0;
// let oldSize = 0;
// let edenCapacity, survivorCapacity, oldCapacity;
// let edenSizeBom = $("size-eden");
// let edenJvmSizeBom = $("jvm-size-eden");
// let oldSizeBom = $("size-old");
// let oldJvmSizeBom = $("jvm-size-old");

// let startIndex = 0;
// let unreachables = [];
// $("start-young").addEventListener("click", () => {
//     if (startIndex >= events.length) return;
//     let event = events[startIndex];
//     console.log(event.type);
//     if (event.type == "VMInit") {
//         eventYoung.innerHTML = "VM Init";
//         infoYoung.innerHTML = "Java VM finished init";
//         // Init variables
//         edenSize = parseInt(event.jvmObjYoungSize);
//         oldSize = 0;
//         edenCapacity = parseInt(event.edenCapacity) + parseInt(event.survivorCapacity);
//         survivorCapacity = event.survivorCapacity;
//         oldCapacity = event.oldCapacity;
//         // Set size
//         setSize(edenSizeBom, event.jvmObjYoungSize, edenCapacity);
//         setJvmSize(edenJvmSizeBom, event.jvmObjYoungSize);
//         setSize(oldSizeBom, 0, oldCapacity);
//     } else if (event.type == "ObjectAlloc") {
//         eventYoung.innerHTML = "Object Allocation";
//         infoYoung.innerHTML = "Finish to create object";
//         let position;
//         if (event.objPosition == "Young") {
//             position = "eden-container";
//         }
//         let objElem = createObjElem($(position), event.tag, event.objSignature, event.objSize, "Unknown");
//         addAppearAnim(objElem);
//         edenContainer.appendChild(objElem);
//         // Add size
//         addEdenSize(event.objSize, edenCapacity);
//     } else if (event.type == "MinorGCStart") {
//         eventYoung.innerHTML = "Minor GC Start";
//         infoYoung.innerHTML = "MinorGCStart";
//     } else if (event.type == "Mark") {
//         // Not only MinorGC has Mark!
//         eventYoung.innerHTML = "GC Mark";
//         infoYoung.innerHTML = "";
//         if (event.objPosition == "Young") {
//             tagUnreachable(edenContainer, event.tag);
//             tagUnreachable(companionSurvivorContainer, event.tag);
//             // Minor size
//             // edenSize -= event.objSize; // TO-DO: 还没有添加上！
//         } else if (event.objPosition == "Old") {
//             tagUnreachable(oldContainer, event.tag);
//         }
//     } else if (event.type == "MinorCopy") {
//         eventYoung.innerHTML = "Minor GC Copy";
//         infoYoung.innerHTML = "MinorGCStart";
//         if (event.copyTo == "S0") {
//             survivorContainer = $("s1-container");
//             companionSurvivorContainer = $("s2-container");
//         } else {
//             survivorContainer = $("s2-container");
//             companionSurvivorContainer = $("s1-container");
//         }
//         copyObjElem(edenContainer, survivorContainer, event.toSObjTags);
//         copyObjElem(companionSurvivorContainer, survivorContainer, event.toSObjTags);
//         copyObjElem(edenContainer, oldContainer, event.toOldObjTags);
//         copyObjElem(companionSurvivorContainer, oldContainer, event.toOldObjTags);
//     } else if (event.type == "MinorDelete") {
//         eventYoung.innerHTML = "Minor GC Delete";
//         infoYoung.innerHTML = "MinorGCStart";
//         // Clear Eden and S
//         for (const objElem of edenContainer.children) {
//             addDisappearAnim(objElem);
//         }
//         for (const objElem of companionSurvivorContainer.children) {
//             addDisappearAnim(objElem);
//         }
//         // 大小控制
//     } else if (event.type == "MinorGCFinish") {
//         eventYoung.innerHTML = "Minor GC Finish";
//         infoYoung.innerHTML = "MinorGCStart";
//         clearEdenAndCompanionElems();
//         // deleteUnreachableElem();
//         unreachables = [];
//     } else if (event.type == "FullGCStart") {
//         eventYoung.innerHTML = "Full GC Start";
//         infoYoung.innerHTML = "MinorGCStart";
//     } else if (event.type == "FullDelete") {
//         eventYoung.innerHTML = "Full GC Delete";
//         infoYoung.innerHTML = "MinorGCStart";
//         for (const objElem of unreachables) {
//             addDisappearAnim(objElem);
//         }
//     } else if (event.type == "FullCompact") {
//         // 删除元素内部内容
//         eventYoung.innerHTML = "Full GC Compact";
//         infoYoung.innerHTML = "MinorGCStart";
//         console.log(unreachables);
//         // 删除Old中不可达对象
//         deleteUnreachableElem(oldContainer);
//         unreachables = [];
//     } else if (event.type == "FullMove") {
//         eventYoung.innerHTML = "Full GC Move";
//         infoYoung.innerHTML = "MinorGCStart";
//         moveObjElem(edenContainer, oldContainer, event.toOldObjTags);
//         moveObjElem(companionSurvivorContainer, oldContainer, event.toOldObjTags);
//         moveObjElem(survivorContainer, oldContainer, event.toOldObjTags);
//     } else if (event.type == "FullGCFinish") {
//         eventYoung.innerHTML = "Full GC Finish";
//         infoYoung.innerHTML = "MinorGCStart";
//         // 删除搬运过来的不可达对象
//         console.log(unreachables);
//         deleteUnreachableElem(edenContainer);
//         deleteUnreachableElem(companionSurvivorContainer);
//         deleteUnreachableElem(survivorContainer);
//         unreachables = [];
//     }
//     startIndex++;
// })

let visualizerElements = new VisualizerElements(
    $("eden-container"), $("s1-container"), $("s2-container"), $("old-container"),
    $("size-eden"), $("size-s1"), $("size-s2"), $("size-old"),
    $("jvm-size-eden"), $("jvm-size-s1"), $("jvm-size-s2"), $("jvm-size-old"),
    $("event-young"), $("info-young")
)

$("start-young").addEventListener("click", () => {
    youngVisualizer.next();
})

$("reset-young").addEventListener("click", () => {
    youngVisualizer.reset();
})